#pragma once
#include<memory>
#include<iostream>
#include<sstream>
#include<cassert>
#include"./Util.hpp"
#include<fstream>
using namespace std;

namespace zlog
{
  class LogSink
  {
   public:
     using ptr = shared_ptr<LogSink>; 
     LogSink(){}
     virtual ~LogSink(){}
     virtual void log(const char* data,size_t len) = 0;
  };
  /* 标准输出落地 */
  class StdoutLogSink : public LogSink
  {
    public:
     void log(const char* data,size_t len)
     {
         cout.write(data,len);
     }
  };
  /* 指定文件落地 */
  class FileLogSink : public LogSink
  {
    public:
       FileLogSink(const string& filename)
       :_filename(filename)
       {
          //1.创建目录
          Util::File::createDirectory(Util::File::path(filename));
          //2.打开文件
          _ofs.open(filename.c_str(),std::ofstream::binary | std::ofstream::app);
          assert(_ofs.is_open() == true);
       }
       void log(const char* data,size_t len)
       {
          _ofs.write(data,len);
          assert(_ofs.good());
       }
    private:
       ofstream _ofs;
       string _filename;
  };
  /* 滚动文件落地 :这里以文件大小滚动*/
  class RollBySizeSink : public LogSink
  {
    public:
       RollBySizeSink(const string& basedir,size_t max_size)
       :_basedir(basedir),
       _max_size(max_size),
       _namecount(0)
       {
         //1.创建目录
         string filename = NewFileName();
         Util::File::createDirectory(Util::File::path(filename));
         //2.创建日志文件
         _ofs.open(filename,std::ofstream::binary | std::ofstream::app);
         assert(_ofs.is_open() == true);
       }

       void log(const char* data,size_t len)
       {
         //1.先判断当前写入的数据大小是否已经超过滚动切换的大小
         if(_cur_size >= _max_size)
         {
             _ofs.close(); //将原文件关闭
             //1.1获取新文件名
             string file = NewFileName();
             Util::File::createDirectory(Util::File::path(file));
             //1.2创建新日志文件
            _ofs.open(file,std::ofstream::binary | std::ofstream::app);
            assert(_ofs.is_open() == true);
            //1.3重置cur
            _cur_size = 0;
         }
         //2.写入文件
           _ofs.write(data,len);
           assert(_ofs.good());
           _cur_size += len;//别忘了更新cur
       }

    private:
       string NewFileName() //根据当前时间戳生成指定文件名
       {
           time_t time = Util::Date::getTime();
           struct tm t;
           localtime_r(&time,&t);
           stringstream  ss;
           ss << _basedir;
           //2002-12-21-13:9:21-1.log
           //日期时间：注意tm_year要+19000 tm_mon要加1
           ss << t.tm_year + 1900 << "-";
           ss << t.tm_mon + 1 <<  "-";
           ss << t.tm_mday << "-";
           ss << t.tm_hour << ":";
           ss << t.tm_min << ":" ;
           ss << t.tm_sec << "-";
           ss << _namecount++ << ".log";
           return ss.str();
       }

    private:
      ofstream _ofs;
      int _namecount;//确定生成的是第几个日志文件
      //通过基础文件名+扩展文件名(时间生成)组成一个实际的当前输出文件名
      string _basedir;// ./log/base- -> ./log/base-20020809122332.log
      size_t _cur_size;//当前文件已经写入的数据大小
      size_t _max_size;//记录最大大小,当前文件超过这个大小就需要切换文件
  };

  /* 时间段类型 */
  enum class TimeGap
  {
     GAP_SECOND,
     GAP_MINUTE,
     GAP_HOUR,
     GAP_DAY
  };
   
  class RollByTimeSink : public LogSink
  {
    public:
      RollByTimeSink(const string& basedir,zlog::TimeGap type)
      :_basedir(basedir),
      _namecount(0)
      {
          //1.先根据时间段类型确定该时间段大小，确定当前是第几个时间段
         switch(type)
         {
            case TimeGap::GAP_SECOND:
               _gap_size = 1;   break;
            case TimeGap::GAP_MINUTE:
               _gap_size = 60;  break;
            case TimeGap::GAP_HOUR:
               _gap_size = 3600; break; 
            case TimeGap::GAP_DAY:
               _gap_size = 3600*24; break;
            default:
               cout << "时间段类型错误" << endl;
               abort();     
         }
          _cur_gap = Util::Date::getTime() / _gap_size;
          cout << "构造:" << _cur_gap << endl;
          //2.创建目录
         string file = NewFileName();
         Util::File::createDirectory(Util::File::path(file));
          //3.创建打开文件
          _ofs.open(file,std::ofstream::binary | std::ofstream::app);
          assert(_ofs.is_open() == true);
      }

      void log(const char* data,size_t len)
      {
          //1.获取当前系统时间判断是否时间段发生变化，变化则切换
          //注意切换后要更新_cur_gap
          time_t cur_time = Util::Date::getTime();
          if((cur_time / _gap_size) != _cur_gap)
          {
            _ofs.close(); //将原文件关闭
             //1.1获取新文件名
             string file = NewFileName();
             Util::File::createDirectory(Util::File::path(file));
             //1.2创建新日志文件
            _ofs.open(file,std::ofstream::binary | std::ofstream::app);
            assert(_ofs.is_open() == true);
            //1.3更新cur_gap
            _cur_gap = cur_time / _gap_size;
            cout << "更新:" << _cur_gap << endl;
          }
           //2.写入文件
           _ofs.write(data,len);
           assert(_ofs.good());
      }

    private:
       string NewFileName() //根据当前时间戳生成指定文件名
       {
           time_t time = Util::Date::getTime();
           struct tm t;
           localtime_r(&time,&t);
           stringstream  ss;
           ss << _basedir;
           //2002-12-21-13:9:21-1.log
           //日期时间：注意tm_year要+19000 tm_mon要加1
           ss << t.tm_year + 1900 << "-";
           ss << t.tm_mon + 1 <<  "-";
           ss << t.tm_mday << "-";
           ss << t.tm_hour << ":";
           ss << t.tm_min << ":" ;
           ss << t.tm_sec << "-";
           ss << _namecount++ << ".log";
           return ss.str();
       }

    private:
      string _basedir;
      int _namecount;
      ofstream _ofs;
      size_t _gap_size; //时间段大小
      size_t _cur_gap; //当前文件的时间段
  };

  /* 落地工厂 */
  class LogSinkFactory
  {
   public:
      template<typename SinkType,typename  ...Args>
      static LogSink::ptr create(Args&&... args)
      {
           return make_shared<SinkType>(std::forward<Args>(args)...);
      }
  };

  

}